home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / source.exe / POSIX / MAKE / COMPAT.C < prev    next >
C/C++ Source or Header  |  1993-07-02  |  20KB  |  701 lines

  1. /*
  2.  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
  3.  * Copyright (c) 1988, 1989 by Adam de Boor
  4.  * Copyright (c) 1989 by Berkeley Softworks
  5.  * All rights reserved.
  6.  *
  7.  * This code is derived from software contributed to Berkeley by
  8.  * Adam de Boor.
  9.  *
  10.  * Redistribution and use in source and binary forms, with or without
  11.  * modification, are permitted provided that the following conditions
  12.  * are met:
  13.  * 1. Redistributions of source code must retain the above copyright
  14.  *    notice, this list of conditions and the following disclaimer.
  15.  * 2. Redistributions in binary form must reproduce the above copyright
  16.  *    notice, this list of conditions and the following disclaimer in the
  17.  *    documentation and/or other materials provided with the distribution.
  18.  * 3. All advertising materials mentioning features or use of this software
  19.  *    must display the following acknowledgement:
  20.  *    This product includes software developed by the University of
  21.  *    California, Berkeley and its contributors.
  22.  * 4. Neither the name of the University nor the names of its contributors
  23.  *    may be used to endorse or promote products derived from this software
  24.  *    without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  */
  38.  
  39. #ifdef DF_POSIX
  40. #define INT_CNT        25    //DF_MSS Internal shell command count.
  41. #define INT_SIZE    20    //DF_MSS Internal shell command max size.    
  42.  
  43. #include <misc.h>
  44. #include <bsdlib.h>
  45. #endif
  46.  
  47. #ifndef lint
  48. static char sccsid[] = "@(#)compat.c    5.7 (Berkeley) 3/1/91";
  49. #endif /* not lint */
  50.  
  51. /*-
  52.  * compat.c --
  53.  *    The routines in this file implement the full-compatibility
  54.  *    mode of PMake. Most of the special functionality of PMake
  55.  *    is available in this mode. Things not supported:
  56.  *        - different shells.
  57.  *        - friendly variable substitution.
  58.  *
  59.  * Interface:
  60.  *    Compat_Run        Initialize things for this module and recreate
  61.  *                      thems as need creatin'
  62.  */
  63.  
  64. #include    <stdlib.h>            /* for getenv() - CSP 21 June 93 */
  65. #include    <stdio.h>
  66. #include    <sys/types.h>
  67. #ifdef _POSIX_SOURCE
  68. #include    <signal.h>
  69. #else
  70. #include    <sys/signal.h>
  71. #endif
  72. #include    <sys/errno.h>
  73. #include    <sys/wait.h>
  74. #include    <ctype.h>
  75. #include    "make.h"
  76. extern int errno;
  77.  
  78. /*
  79.  * The following array is used to make a fast determination of which
  80.  * characters are interpreted specially by the shell.  If a command
  81.  * contains any of these characters, it is executed by the shell, not
  82.  * directly by us.
  83.  */
  84.  
  85. static char         meta[256];
  86.  
  87. static GNode        *curTarg = NILGNODE;
  88. static GNode        *ENDNode;
  89. static int          CompatRunCommand();
  90.  
  91. /*-
  92.  *-----------------------------------------------------------------------
  93.  * CompatInterrupt --
  94.  *    Interrupt the creation of the current target and remove it if
  95.  *    it ain't precious.
  96.  *
  97.  * Results:
  98.  *    None.
  99.  *
  100.  * Side Effects:
  101.  *    The target is removed and the process exits. If .INTERRUPT exists,
  102.  *    its commands are run first WITH INTERRUPTS IGNORED..
  103.  *
  104.  *-----------------------------------------------------------------------
  105.  */
  106. static void
  107. CompatInterrupt (signo)
  108.     int        signo;
  109. {
  110.     GNode   *gn;
  111.     
  112.     if ((curTarg != NILGNODE) && !Targ_Precious (curTarg)) {
  113.     char       *file = Var_Value (TARGET, curTarg);
  114.  
  115.     if (unlink (file) == SUCCESS) {
  116.         printf ("*** %s removed\n", file);
  117.     }
  118.  
  119.     /*
  120.      * Run .INTERRUPT only if hit with interrupt signal
  121.      */
  122.     if (signo == SIGINT) {
  123.         gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
  124.         if (gn != NILGNODE) {
  125.         Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn);
  126.         }
  127.     }
  128.     }
  129.     exit (0);
  130. }
  131.  
  132. /*-
  133.  *-----------------------------------------------------------------------
  134.  * CompatRunCommand --
  135.  *    Execute the next command for a target. If the command returns an
  136.  *    error, the node's made field is set to ERROR and creation stops.
  137.  *
  138.  * Results:
  139.  *    0 if the command succeeded, 1 if an error occurred.
  140.  *
  141.  * Side Effects:
  142.  *    The node's 'made' field may be set to ERROR.
  143.  *
  144.  *-----------------------------------------------------------------------
  145.  */
  146. static int
  147. CompatRunCommand (cmd, gn)
  148.     char          *cmd;            /* Command to execute */
  149.     GNode         *gn;        /* Node from which the command came */
  150. {
  151. #ifdef DF_POSIX
  152.     int       j;        //DF_MSS local iterator var.
  153.     int       shellCmd;    //DF_MSS flag for shell commands.
  154.  
  155.                 //DF_MSS internal shell commands.
  156.     char      intCmds[INT_CNT][INT_SIZE] = 
  157. //          {"echo", "cd", "ar", "ld", "ls"};
  158.           {"echo", "ar", "cat", "cc", "chgrp", "chmod", "chown",
  159.             "cp", "find", "grep", "ld", "ln", "ls", "make",
  160.             "mkdir", "sh", "touch", "vi", "wc"};
  161.  
  162.  
  163.     char      command[256]; //DF_MSS strcpying cmd to this because of 
  164.                 //term lock up
  165. #endif
  166.  
  167.     char          *cmdStart;    /* Start of expanded command */
  168.     register char *cp;
  169.     Boolean       silent,       /* Don't print command */
  170.           errCheck;     /* Check errors */
  171.     union wait       reason;       /* Reason for child's death */
  172.     int              status;       /* Description of child's death */
  173.     int              cpid;            /* Child actually found */
  174.     int              numWritten;    /* Number of bytes written for error message */
  175.     ReturnStatus  stat;            /* Status of fork */
  176.     LstNode       cmdNode;      /* Node where current command is located */
  177.     char          **av;            /* Argument vector for thing to exec */
  178.     int              argc;            /* Number of arguments in av or 0 if not
  179.                  * dynamically allocated */
  180.     Boolean       local;        /* TRUE if command should be executed
  181.                  * locally */
  182.     register
  183.         i;
  184.  
  185.     silent = gn->type & OP_SILENT;
  186.     errCheck = !(gn->type & OP_IGNORE);
  187.  
  188.     cmdNode = Lst_Member (gn->commands, (ClientData)cmd);
  189.     cmdStart = Var_Subst (cmd, gn, FALSE);
  190.  
  191.     /*
  192.      * brk_string will return an argv with a NULL in av[1], thus causing
  193.      * execvp to choke and die horribly. Besides, how can we execute a null
  194.      * command? In any case, we warn the user that the command expanded to
  195.      * nothing (is this the right thing to do?).
  196.      */
  197.      
  198.     if (*cmdStart == '\0') {
  199.     Error("%s expands to empty string", cmd);
  200.     return(0);
  201.     } else {
  202.     cmd = cmdStart;
  203.     }
  204.     Lst_Replace (cmdNode, (ClientData)cmdStart);
  205.  
  206.     if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
  207.     (void)Lst_AtEnd(ENDNode->commands, (ClientData)cmdStart);
  208.     return(0);
  209.     } else if (strcmp(cmdStart, "...") == 0) {
  210.     gn->type |= OP_SAVE_CMDS;
  211.     return(0);
  212.     }
  213.  
  214.     while ((*cmd == '@') || (*cmd == '-')) {
  215.     if (*cmd == '@') {
  216.         silent = TRUE;
  217.     } else {
  218.         errCheck = FALSE;
  219.     }
  220.     cmd++;
  221.     }
  222.     
  223.     /*
  224.      * Search for meta characters in the command. If there are no meta
  225.      * characters, there's no need to execute a shell to execute the
  226.      * command.
  227.      */
  228.     for (cp = cmd; !meta[*cp]; cp++) {
  229.     continue;
  230.     }
  231.  
  232.     /*
  233.      * Print the command before echoing if we're not supposed to be quiet for
  234.      * this one. We also print the command if -n given.
  235.      */
  236.     if (!silent || noExecute) {
  237.     printf ("cmd1 %s\n", cmd);
  238.     fflush(stdout);
  239.     }
  240.  
  241.     /*
  242.      * If we're not supposed to execute any commands, this is as far as
  243.      * we go...
  244.      */
  245.     if (noExecute) {
  246.     return (0);
  247.     }
  248.  
  249. #ifdef DF_POSIX
  250.     shellCmd = 0;            //DF_MSS Check for internal shell cmds
  251.  
  252.     strcpy(command, cmd);
  253.  
  254.     for(j = 0; j < INT_CNT; j++)
  255.     if(strstr(command, intCmds[j]))
  256.         shellCmd = 1;
  257.     
  258.     if (*cp != '\0' || shellCmd) {
  259. #else
  260.     if (*cp != '\0') {
  261. #endif
  262.     /*
  263.      * If *cp isn't the null character, we hit a "meta" character and
  264.      * need to pass the command off to the shell. We give the shell the
  265.      * -e flag as well as -c if it's supposed to exit when it hits an
  266.      * error.
  267.      */
  268. #if 0 /* MASKED OUT FOR TESTING */
  269.     static char    *shargv[4] = { "/usr/bin/sh.exe" };
  270. #else
  271.     static char    *shargv[4] = { "/mybin/sh.exe" };
  272. #endif
  273.  
  274.         if (( shargv[0] = getenv(ENV_SH)) == ( char * ) NULL )
  275.            return 0;
  276.  
  277.     shargv[1] = (errCheck ? "-ec" : "-c");
  278.  
  279. //    shargv[2] = cmd;
  280.     strcpy(command, cmd); //DF_MSS suspect memory overflow when execced.
  281.     shargv[2] = command;
  282.  
  283.     shargv[3] = (char *)NULL;
  284.     av = shargv;
  285.     argc = 0;
  286.     } else {
  287.     /*
  288.      * No meta-characters, so no need to exec a shell. Break the command
  289.      * into words to form an argument vector we can execute.
  290.      * brk_string sticks our name in av[0], so we have to
  291.      * skip over it...
  292.      */
  293.     av = brk_string(cmd, &argc);
  294.     av += 1;
  295.     }
  296.     
  297.     local = TRUE;
  298.  
  299.     /*
  300.      * Fork and execute the single command. If the fork fails, we abort.
  301.      */
  302.     cpid = vfork();
  303.     if (cpid < 0) {
  304.     Fatal("Could not fork");
  305.     }
  306.     if (cpid == 0) {
  307.     if (local) {
  308.  
  309. #ifdef TRACE
  310.         register
  311.             j;
  312.  
  313.         for (j = 0; av [j]; j++)
  314.             printf ("arg %d - X%sX\n", j, av [j]);
  315. #endif
  316.  
  317. #ifdef TRACE
  318. puts("b execvp compat.c");
  319. for(i=0; av[i] ; i++)
  320.     printf("ss X%sX\n", av[i]);
  321. #endif
  322.  
  323.             if ( !strcmp(av[0],"/bin/sh" ) )
  324.             {
  325.                char *ptr;
  326.          
  327.                if (( ptr = getenv("SHELL")) != ( char * ) NULL )
  328.                       strcpy(av[0],ptr);
  329.             }
  330.             execvp(av[0], av);
  331.  
  332. #ifdef TRACE
  333. puts("a execvp compat.c");
  334. #endif
  335.         numWritten = write (2, av[0], strlen (av[0]));
  336.         numWritten = write (2, ": not found\n", sizeof(": not found"));
  337.     } else {
  338.         (void)execv(av[0], av);
  339.     }
  340.     exit(1);
  341.     }
  342.     
  343.     /*
  344.      * The child is off and running. Now all we can do is wait...
  345.      */
  346.     while (1) {
  347.     int       id;
  348.  
  349.     if (!local) {
  350.         id = 0;
  351.     }
  352.  
  353.     while ((stat = wait((int *)&reason)) != cpid) {
  354.         if (stat == -1 && errno != EINTR) {
  355.         break;
  356.         }
  357.     }
  358.  
  359.     if (stat > -1) {
  360.         if (WIFSTOPPED(reason.w_status)) {
  361. #ifdef _POSIX_SOURCE
  362.         status = reason.w_T.w_retcode;        /* stopped */
  363. #else
  364.         status = reason.w_T.w_stopval;        /* stopped */
  365. #endif
  366.     
  367.         } else if (WIFEXITED(reason.w_status)) {
  368.         status = reason.w_T.w_retcode;        /* exited */
  369.         if (status != 0) {
  370.             printf ("*** Error code %d", status);
  371.         }
  372.         } else {
  373.         status = reason.w_T.w_termsig;        /* signaled */
  374.         printf ("*** Signal %d", status);
  375.         } 
  376.         
  377.         if (!WIFEXITED(reason.w_status) || (status != 0)) {
  378.         if (errCheck) {
  379.             gn->made = ERROR;
  380.             if (keepgoing) {
  381.             /*
  382.              * Abort the current target, but let others
  383.              * continue.
  384.              */
  385.             printf (" (continuing)\n");
  386.             }
  387.         } else {
  388.             /*
  389.              * Continue executing commands for this target.
  390.              * If we return 0, this will happen...
  391.              */
  392.             printf (" (ignored)\n");
  393.             status = 0;
  394.         }
  395.         }
  396.         break;
  397.     } else {
  398.         Fatal ("error in wait: %d", stat);
  399.         /*NOTREACHED*/
  400.     }
  401.     }
  402.  
  403.     return (status);
  404. }
  405.  
  406. /*-
  407.  *-----------------------------------------------------------------------
  408.  * CompatMake --
  409.  *    Make a target.
  410.  *
  411.  * Results:
  412.  *    0
  413.  *
  414.  * Side Effects:
  415.  *    If an error is detected and not being ignored, the process exits.
  416.  *
  417.  *-----------------------------------------------------------------------
  418.  */
  419. static int
  420. CompatMake (gn, pgn)
  421.     GNode         *gn;        /* The node to make */
  422.     GNode         *pgn;        /* Parent to abort if necessary */
  423. {
  424.     if (gn->type & OP_USE) {
  425.     Make_HandleUse(gn, pgn);
  426.     } else if (gn->made == UNMADE) {
  427.     /*
  428.      * First mark ourselves to be made, then apply whatever transformations
  429.      * the suffix module thinks are necessary. Once that's done, we can
  430.      * descend and make all our children. If any of them has an error
  431.      * but the -k flag was given, our 'make' field will be set FALSE again.
  432.      * This is our signal to not attempt to do anything but abort our
  433.      * parent as well.
  434.      */
  435.     gn->make = TRUE;
  436.     gn->made = BEINGMADE;
  437.     Suff_FindDeps (gn);
  438.     Lst_ForEach (gn->children, CompatMake, (ClientData)gn);
  439.     if (!gn->make) {
  440.         gn->made = ABORTED;
  441.         pgn->make = FALSE;
  442.         return (0);
  443.     }
  444.  
  445.     if (Lst_Member (gn->iParents, pgn) != NILLNODE) {
  446.         Var_Set (IMPSRC, Var_Value(TARGET, gn), pgn);
  447.     }
  448.     
  449.     /*
  450.      * All the children were made ok. Now cmtime contains the modification
  451.      * time of the newest child, we need to find out if we exist and when
  452.      * we were modified last. The criteria for datedness are defined by the
  453.      * Make_OODate function.
  454.      */
  455.     if (DEBUG(MAKE)) {
  456.         printf("Examining %s...", gn->name);
  457.     }
  458.     if (! Make_OODate(gn)) {
  459.         gn->made = UPTODATE;
  460.         if (DEBUG(MAKE)) {
  461.         printf("up-to-date.\n");
  462.         }
  463.         return (0);
  464.     } else if (DEBUG(MAKE)) {
  465.         printf("out-of-date.\n");
  466.     }
  467.  
  468.     /*
  469.      * If the user is just seeing if something is out-of-date, exit now
  470.      * to tell him/her "yes".
  471.      */
  472.     if (queryFlag) {
  473.         exit (-1);
  474.     }
  475.  
  476.     /*
  477.      * We need to be re-made. We also have to make sure we've got a $?
  478.      * variable. To be nice, we also define the $> variable using
  479.      * Make_DoAllVar().
  480.      */
  481.     Make_DoAllVar(gn);
  482.             
  483.     /*
  484.      * Alter our type to tell if errors should be ignored or things
  485.      * should not be printed so CompatRunCommand knows what to do.
  486.      */
  487.     if (Targ_Ignore (gn)) {
  488.         gn->type |= OP_IGNORE;
  489.     }
  490.     if (Targ_Silent (gn)) {
  491.         gn->type |= OP_SILENT;
  492.     }
  493.  
  494.     if (Job_CheckCommands (gn, Fatal)) {
  495.         /*
  496.          * Our commands are ok, but we still have to worry about the -t
  497.          * flag...
  498.          */
  499.         if (!touchFlag) {
  500.         curTarg = gn;
  501.         Lst_ForEach (gn->commands, CompatRunCommand, (ClientData)gn);
  502.         curTarg = NILGNODE;
  503.         } else {
  504.         Job_Touch (gn, gn->type & OP_SILENT);
  505.         }
  506.     } else {
  507.         gn->made = ERROR;
  508.     }
  509.  
  510.     if (gn->made != ERROR) {
  511.         /*
  512.          * If the node was made successfully, mark it so, update
  513.          * its modification time and timestamp all its parents. Note
  514.          * that for .ZEROTIME targets, the timestamping isn't done.
  515.          * This is to keep its state from affecting that of its parent.
  516.          */
  517.         gn->made = MADE;
  518. #ifndef RECHECK
  519.         /*
  520.          * We can't re-stat the thing, but we can at least take care of
  521.          * rules where a target depends on a source that actually creates
  522.          * the target, but only if it has changed, e.g.
  523.          *
  524.          * parse.h : parse.o
  525.          *
  526.          * parse.o : parse.y
  527.          *      yacc -d parse.y
  528.          *      cc -c y.tab.c
  529.          *      mv y.tab.o parse.o
  530.          *      cmp -s y.tab.h parse.h || mv y.tab.h parse.h
  531.          *
  532.          * In this case, if the definitions produced by yacc haven't
  533.          * changed from before, parse.h won't have been updated and
  534.          * gn->mtime will reflect the current modification time for
  535.          * parse.h. This is something of a kludge, I admit, but it's a
  536.          * useful one..
  537.          *
  538.          * XXX: People like to use a rule like
  539.          *
  540.          * FRC:
  541.          *
  542.          * To force things that depend on FRC to be made, so we have to
  543.          * check for gn->children being empty as well...
  544.          */
  545.         if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) {
  546.         gn->mtime = now;
  547.         }
  548. #else
  549.         /*
  550.          * This is what Make does and it's actually a good thing, as it
  551.          * allows rules like
  552.          *
  553.          *    cmp -s y.tab.h parse.h || cp y.tab.h parse.h
  554.          *
  555.          * to function as intended. Unfortunately, thanks to the stateless
  556.          * nature of NFS (and the speed of this program), there are times
  557.          * when the modification time of a file created on a remote
  558.          * machine will not be modified before the stat() implied by
  559.          * the Dir_MTime occurs, thus leading us to believe that the file
  560.          * is unchanged, wreaking havoc with files that depend on this one.
  561.          *
  562.          * I have decided it is better to make too much than to make too
  563.          * little, so this stuff is commented out unless you're sure it's
  564.          * ok.
  565.          * -- ardeb 1/12/88
  566.          */
  567.         if (noExecute || Dir_MTime(gn) == 0) {
  568.         gn->mtime = now;
  569.         }
  570.         if (DEBUG(MAKE)) {
  571.         printf("update time: %s\n", Targ_FmtTime(gn->mtime));
  572.         }
  573. #endif
  574.         if (!(gn->type & OP_EXEC)) {
  575.         pgn->childMade = TRUE;
  576.         Make_TimeStamp(pgn, gn);
  577.         }
  578.     } else if (keepgoing) {
  579.         pgn->make = FALSE;
  580.     } else {
  581.         printf ("\n\nStop.\n");
  582.         exit (1);
  583.     }
  584.     } else if (gn->made == ERROR) {
  585.     /*
  586.      * Already had an error when making this beastie. Tell the parent
  587.      * to abort.
  588.      */
  589.     pgn->make = FALSE;
  590.     } else {
  591.     if (Lst_Member (gn->iParents, pgn) != NILLNODE) {
  592.         Var_Set (IMPSRC, Var_Value(TARGET, gn), pgn);
  593.     }
  594.     switch(gn->made) {
  595.         case BEINGMADE:
  596.         Error("Graph cycles through %s\n", gn->name);
  597.         gn->made = ERROR;
  598.         pgn->make = FALSE;
  599.         break;
  600.         case MADE:
  601.         if ((gn->type & OP_EXEC) == 0) {
  602.             pgn->childMade = TRUE;
  603.             Make_TimeStamp(pgn, gn);
  604.         }
  605.         break;
  606.         case UPTODATE:
  607.         if ((gn->type & OP_EXEC) == 0) {
  608.             Make_TimeStamp(pgn, gn);
  609.         }
  610.         break;
  611.     }
  612.     }
  613.  
  614.     return (0);
  615. }
  616.     
  617. /*-
  618.  *-----------------------------------------------------------------------
  619.  * Compat_Run --
  620.  *    Initialize this mode and start making.
  621.  *
  622.  * Results:
  623.  *    None.
  624.  *
  625.  * Side Effects:
  626.  *    Guess what?
  627.  *
  628.  *-----------------------------------------------------------------------
  629.  */
  630. void
  631. Compat_Run(targs)
  632.     Lst              targs;    /* List of target nodes to re-create */
  633. {
  634.     char          *cp;        /* Pointer to string of shell meta-characters */
  635.     GNode         *gn;        /* Current root target */
  636.     int              errors;   /* Number of targets not remade due to errors */
  637.  
  638.     if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
  639.     signal(SIGINT, CompatInterrupt);
  640.     }
  641.     if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
  642.     signal(SIGTERM, CompatInterrupt);
  643.     }
  644.     if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
  645.     signal(SIGHUP, CompatInterrupt);
  646.     }
  647.     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
  648.     signal(SIGQUIT, CompatInterrupt);
  649.     }
  650.  
  651.     for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) {
  652.     meta[*cp] = 1;
  653.     }
  654.     /*
  655.      * The null character serves as a sentinel in the string.
  656.      */
  657.     meta[0] = 1;
  658.  
  659.     ENDNode = Targ_FindNode(".END", TARG_CREATE);
  660.     /*
  661.      * If the user has defined a .BEGIN target, execute the commands attached
  662.      * to it.
  663.      */
  664.     if (!queryFlag) {
  665.     gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);
  666.     if (gn != NILGNODE) {
  667.         Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn);
  668.     }
  669.     }
  670.  
  671.     /*
  672.      * For each entry in the list of targets to create, call CompatMake on
  673.      * it to create the thing. CompatMake will leave the 'made' field of gn
  674.      * in one of several states:
  675.      *        UPTODATE        gn was already up-to-date
  676.      *        MADE          gn was recreated successfully
  677.      *        ERROR         An error occurred while gn was being created
  678.      *        ABORTED        gn was not remade because one of its inferiors
  679.      *                      could not be made due to errors.
  680.      */
  681.     errors = 0;
  682.     while (!Lst_IsEmpty (targs)) {
  683.     gn = (GNode *) Lst_DeQueue (targs);
  684.     CompatMake (gn, gn);
  685.  
  686.     if (gn->made == UPTODATE) {
  687.         printf ("`%s' is up to date.\n", gn->name);
  688.     } else if (gn->made == ABORTED) {
  689.         printf ("`%s' not remade because of errors.\n", gn->name);
  690.         errors += 1;
  691.     }
  692.     }
  693.  
  694.     /*
  695.      * If the user has defined a .END target, run its commands.
  696.      */
  697.     if (errors == 0) {
  698.     Lst_ForEach(ENDNode->commands, CompatRunCommand, (ClientData)gn);
  699.     }
  700. }
  701.